home *** CD-ROM | disk | FTP | other *** search
/ Amiga Games Extra 1996 September / Amiga Games Extra CD-ROM 9-1996.iso / userbox / publicdomain / vim-4.2 / src / csearch.c < prev    next >
C/C++ Source or Header  |  1996-06-09  |  17KB  |  690 lines

  1. /* vi:set ts=4 sw=4:
  2.  *
  3.  * VIM - Vi IMproved        by Bram Moolenaar
  4.  *
  5.  * Do ":help uganda"  in Vim to read copying and usage conditions.
  6.  * Do ":help credits" in Vim to see a list of people who contributed.
  7.  */
  8.  
  9. /*
  10.  *
  11.  * csearch.c: do_sub() and do_glob() for :s, :g and :v
  12.  */
  13.  
  14. #include "vim.h"
  15. #include "globals.h"
  16. #include "proto.h"
  17. #include "option.h"
  18.  
  19. /* we use modified Henry Spencer's regular expression routines */
  20. #include "regexp.h"
  21.  
  22. static int do_sub_msg __ARGS((void));
  23.  
  24. #ifdef VIMINFO
  25.     static char_u   *old_sub = NULL;
  26. #endif /* VIMINFO */
  27.  
  28. /* do_sub(lp, up, cmd)
  29.  *
  30.  * Perform a substitution from line 'lp' to line 'up' using the
  31.  * command pointed to by 'cmd' which should be of the form:
  32.  *
  33.  * /pattern/substitution/gc
  34.  *
  35.  * The trailing 'g' is optional and, if present, indicates that multiple
  36.  * substitutions should be performed on each line, if applicable.
  37.  * The trailing 'c' is optional and, if present, indicates that a confirmation
  38.  * will be asked for each replacement.
  39.  * The usual escapes are supported as described in the regexp docs.
  40.  *
  41.  * use_old == 0 for :substitute
  42.  * use_old == 1 for :&
  43.  * use_old == 2 for :~
  44.  */
  45.  
  46. static long            sub_nsubs;        /* total number of substitutions */
  47. static linenr_t        sub_nlines;        /* total number of lines changed */
  48.  
  49.     void
  50. do_sub(lp, up, cmd, nextcommand, use_old)
  51.     linenr_t    lp;
  52.     linenr_t    up;
  53.     char_u        *cmd;
  54.     char_u        **nextcommand;
  55.     int            use_old;
  56. {
  57.     linenr_t        lnum;
  58.     long            i;
  59.     char_u           *ptr;
  60.     char_u           *old_line;
  61.     regexp           *prog;
  62.     static int        do_all = FALSE;     /* do multiple substitutions per line */
  63.     static int        do_ask = FALSE;     /* ask for confirmation */
  64.     int                do_print = FALSE;    /* print last line with subst. */
  65.     char_u           *pat, *sub;
  66. #ifndef VIMINFO                        /* otherwise it is global */
  67.     static char_u   *old_sub = NULL;
  68. #endif
  69.     int             delimiter;
  70.     int             sublen;
  71.     int                got_quit = FALSE;
  72.     int                got_match = FALSE;
  73.     int                temp;
  74.     int                which_pat;
  75.     
  76.     if (!global_busy)
  77.     {
  78.         sub_nsubs = 0;
  79.         sub_nlines = 0;
  80.     }
  81.  
  82.     if (use_old == 2)
  83.         which_pat = RE_LAST;    /* use last used regexp */
  84.     else
  85.         which_pat = RE_SUBST;    /* use last substitute regexp */
  86.  
  87.                                 /* new pattern and substitution */
  88.     if (use_old == 0 && *cmd != NUL &&
  89.                        vim_strchr((char_u *)"0123456789gcr|\"", *cmd) == NULL)
  90.     {
  91.                                 /* don't accept alphanumeric for separator */
  92.         if (isalpha(*cmd) || isdigit(*cmd))
  93.         {
  94.             EMSG("Regular expressions can't be delimited by letters or digits");
  95.             return;
  96.         }
  97.         /*
  98.          * undocumented vi feature:
  99.          *    "\/sub/" and "\?sub?" use last used search pattern (almost like
  100.          *    //sub/r).  "\&sub&" use last substitute pattern (like //sub/).
  101.          */
  102.         if (*cmd == '\\')
  103.         {
  104.             ++cmd;
  105.             if (vim_strchr((char_u *)"/?&", *cmd) == NULL)
  106.             {
  107.                 emsg(e_backslash);
  108.                 return;
  109.             }
  110.             if (*cmd != '&')
  111.                 which_pat = RE_SEARCH;        /* use last '/' pattern */
  112.             pat = (char_u *)"";                /* empty search pattern */
  113.             delimiter = *cmd++;                /* remember delimiter character */
  114.         }
  115.         else            /* find the end of the regexp */
  116.         {
  117.             which_pat = RE_LAST;            /* use last used regexp */
  118.             delimiter = *cmd++;                /* remember delimiter character */
  119.             pat = cmd;                        /* remember start of search pat */
  120.             cmd = skip_regexp(cmd, delimiter);
  121.             if (cmd[0] == delimiter)        /* end delimiter found */
  122.                 *cmd++ = NUL;                /* replace it with a NUL */
  123.         }
  124.  
  125.         /*
  126.          * Small incompatibility: vi sees '\n' as end of the command, but in
  127.          * Vim we want to use '\n' to find/substitute a NUL.
  128.          */
  129.         sub = cmd;            /* remember the start of the substitution */
  130.  
  131.         while (cmd[0])
  132.         {
  133.             if (cmd[0] == delimiter)            /* end delimiter found */
  134.             {
  135.                 *cmd++ = NUL;                    /* replace it with a NUL */
  136.                 break;
  137.             }
  138.             if (cmd[0] == '\\' && cmd[1] != 0)    /* skip escaped characters */
  139.                 ++cmd;
  140.             ++cmd;
  141.         }
  142.  
  143.         vim_free(old_sub);
  144.         old_sub = strsave(sub);
  145.     }
  146.     else                                /* use previous pattern and substitution */
  147.     {
  148.         if (old_sub == NULL)    /* there is no previous command */
  149.         {
  150.             emsg(e_nopresub);
  151.             return;
  152.         }
  153.         pat = NULL;             /* myregcomp() will use previous pattern */
  154.         sub = old_sub;
  155.     }
  156.  
  157.     /*
  158.      * find trailing options
  159.      */
  160.     if (!p_ed)
  161.     {
  162.         if (p_gd)                /* default is global on */
  163.             do_all = TRUE;
  164.         else
  165.             do_all = FALSE;
  166.         do_ask = FALSE;
  167.     }
  168.     while (*cmd)
  169.     {
  170.         /*
  171.          * Note that 'g' and 'c' are always inverted, also when p_ed is off
  172.          * 'r' is never inverted.
  173.          */
  174.         if (*cmd == 'g')
  175.             do_all = !do_all;
  176.         else if (*cmd == 'c')
  177.             do_ask = !do_ask;
  178.         else if (*cmd == 'r')        /* use last used regexp */
  179.             which_pat = RE_LAST;
  180.         else if (*cmd == 'p')
  181.             do_print = TRUE;
  182.         else
  183.             break;
  184.         ++cmd;
  185.     }
  186.  
  187.     /*
  188.      * check for a trailing count
  189.      */
  190.     cmd = skipwhite(cmd);
  191.     if (isdigit(*cmd))
  192.     {
  193.         i = getdigits(&cmd);
  194.         if (i <= 0)
  195.         {
  196.             emsg(e_zerocount);
  197.             return;
  198.         }
  199.         lp = up;
  200.         up += i - 1;
  201.     }
  202.  
  203.     /*
  204.      * check for trailing '|', '"' or '\n'
  205.      */
  206.     cmd = skipwhite(cmd);
  207.     if (*cmd)
  208.     {
  209.         if (vim_strchr((char_u *)"|\"\n", *cmd) == NULL)
  210.         {
  211.             emsg(e_trailing);
  212.             return;
  213.         }
  214.         else
  215.             *nextcommand = cmd + 1;
  216.     }
  217.  
  218.     if ((prog = myregcomp(pat, RE_SUBST, which_pat, SEARCH_HIS)) == NULL)
  219.     {
  220.         emsg(e_invcmd);
  221.         return;
  222.     }
  223.  
  224.     /*
  225.      * ~ in the substitute pattern is replaced with the old pattern.
  226.      * We do it here once to avoid it to be replaced over and over again.
  227.      */
  228.     sub = regtilde(sub, (int)p_magic);
  229.  
  230.     old_line = NULL;
  231.     for (lnum = lp; lnum <= up && !(got_int || got_quit); ++lnum)
  232.     {
  233.         ptr = ml_get(lnum);
  234.         if (vim_regexec(prog, ptr, TRUE))  /* a match on this line */
  235.         {
  236.             char_u        *new_end, *new_start = NULL;
  237.             char_u        *old_match, *old_copy;
  238.             char_u        *prev_old_match = NULL;
  239.             char_u        *p1;
  240.             int            did_sub = FALSE;
  241.             int            match, lastone;
  242.             unsigned    len, needed_len;
  243.             unsigned    new_start_len = 0;
  244.  
  245.             /* make a copy of the line, so it won't be taken away when updating
  246.                 the screen */
  247.             if ((old_line = strsave(ptr)) == NULL)
  248.                 continue;
  249.             vim_regexec(prog, old_line, TRUE);  /* match again on this line to
  250.                                                   * update the pointers. TODO:
  251.                                                  * remove extra vim_regexec() */
  252.             if (!got_match)
  253.             {
  254.                 setpcmark();
  255.                 got_match = TRUE;
  256.             }
  257.  
  258.             old_copy = old_match = old_line;
  259.             for (;;)            /* loop until nothing more to replace */
  260.             {
  261.                 /*
  262.                  * Save the position of the last change for the final cursor
  263.                  * position (just like the real vi).
  264.                  */
  265.                 curwin->w_cursor.lnum = lnum;
  266.                 curwin->w_cursor.col = (int)(prog->startp[0] - old_line);
  267.  
  268.                 /*
  269.                  * Match empty string does not count, except for first match.
  270.                  * This reproduces the strange vi behaviour.
  271.                  * This also catches endless loops.
  272.                  */
  273.                 if (old_match == prev_old_match && old_match == prog->endp[0])
  274.                 {
  275.                     ++old_match;
  276.                     goto skip;
  277.                 }
  278.                 old_match = prog->endp[0];
  279.                 prev_old_match = old_match;
  280.  
  281.                 while (do_ask)        /* loop until 'y', 'n', 'q', CTRL-E or CTRL-Y typed */
  282.                 {
  283.                     temp = RedrawingDisabled;
  284.                     RedrawingDisabled = FALSE;
  285.                     comp_Botline(curwin);
  286.                     search_match_len = prog->endp[0] - prog->startp[0];
  287.                                     /* invert the matched string
  288.                                      * remove the inversion afterwards */
  289.                     if (search_match_len == 0)
  290.                         search_match_len = 1;        /* show something! */
  291.                     highlight_match = TRUE;
  292.                     updateScreen(CURSUPD);
  293.                     highlight_match = FALSE;
  294.                     redraw_later(NOT_VALID);
  295.                                     /* same highlighting as for wait_return */
  296.                     (void)set_highlight('r');
  297.                     msg_highlight = TRUE;
  298.                     smsg((char_u *)"replace with %s (y/n/a/q/^E/^Y)?", sub);
  299.                     showruler(TRUE);
  300.                     RedrawingDisabled = temp;
  301.                     
  302.                     ++no_mapping;                /* don't map this key */
  303.                     i = vgetc();
  304.                     --no_mapping;
  305.  
  306.                         /* clear the question */
  307.                     msg_didout = FALSE;            /* don't scroll up */
  308.                     msg_col = 0;
  309.                     gotocmdline(TRUE);
  310.                     if (i == 'q' || i == ESC || i == Ctrl('C'))
  311.                     {
  312.                         got_quit = TRUE;
  313.                         break;
  314.                     }
  315.                     else if (i == 'n')
  316.                         goto skip;
  317.                     else if (i == 'y')
  318.                         break;
  319.                     else if (i == 'a')
  320.                     {
  321.                         do_ask = FALSE;
  322.                         break;
  323.                     }
  324.                     else if (i == Ctrl('E'))
  325.                         scrollup_clamp();
  326.                     else if (i == Ctrl('Y'))
  327.                         scrolldown_clamp();
  328.                 }
  329.                 if (got_quit)
  330.                     break;
  331.  
  332.                         /* get length of substitution part */
  333.                 sublen = vim_regsub(prog, sub, old_line, FALSE, (int)p_magic);
  334.                 if (new_start == NULL)
  335.                 {
  336.                     /*
  337.                      * Get some space for a temporary buffer to do the
  338.                      * substitution into (and some extra space to avoid
  339.                      * too many calls to alloc()/free()).
  340.                      */
  341.                     new_start_len = STRLEN(old_copy) + sublen + 25;
  342.                     if ((new_start = alloc_check(new_start_len)) == NULL)
  343.                         goto outofmem;
  344.                     *new_start = NUL;
  345.                     new_end = new_start;
  346.                 }
  347.                 else
  348.                 {
  349.                     /*
  350.                      * Extend the temporary buffer to do the substitution into.
  351.                      * Avoid an alloc()/free(), it takes a lot of time.
  352.                      */
  353.                     len = STRLEN(new_start);
  354.                     needed_len = len + STRLEN(old_copy) + sublen + 1;
  355.                     if (needed_len > new_start_len)
  356.                     {
  357.                         needed_len += 20;        /* get some extra */
  358.                         if ((p1 = alloc_check(needed_len)) == NULL)
  359.                             goto outofmem;
  360.                         STRCPY(p1, new_start);
  361.                         vim_free(new_start);
  362.                         new_start = p1;
  363.                         new_start_len = needed_len;
  364.                     }
  365.                     new_end = new_start + len;
  366.                 }
  367.  
  368.                 /*
  369.                  * copy the text up to the part that matched
  370.                  */
  371.                 i = prog->startp[0] - old_copy;
  372.                 vim_memmove(new_end, old_copy, (size_t)i);
  373.                 new_end += i;
  374.  
  375.                 vim_regsub(prog, sub, new_end, TRUE, (int)p_magic);
  376.                 sub_nsubs++;
  377.                 did_sub = TRUE;
  378.  
  379.                 /*
  380.                  * Now the trick is to replace CTRL-Ms with a real line break.
  381.                  * This would make it impossible to insert CTRL-Ms in the text.
  382.                  * That is the way vi works. In Vim the line break can be
  383.                  * avoided by preceding the CTRL-M with a CTRL-V. Now you can't
  384.                  * precede a line break with a CTRL-V, big deal.
  385.                  */
  386.                 while ((p1 = vim_strchr(new_end, CR)) != NULL)
  387.                 {
  388.                     if (p1 == new_end || p1[-1] != Ctrl('V'))
  389.                     {
  390.                         if (u_inssub(lnum) == OK)    /* prepare for undo */
  391.                         {
  392.                             *p1 = NUL;                /* truncate up to the CR */
  393.                             mark_adjust(lnum, MAXLNUM, 1L, 0L);
  394.                             ml_append(lnum - 1, new_start,
  395.                                         (colnr_t)(p1 - new_start + 1), FALSE);
  396.                             ++lnum;
  397.                             ++up;                /* number of lines increases */
  398.                             STRCPY(new_start, p1 + 1);    /* copy the rest */
  399.                             new_end = new_start;
  400.                         }
  401.                     }
  402.                     else                            /* remove CTRL-V */
  403.                     {
  404.                         STRCPY(p1 - 1, p1);
  405.                         new_end = p1;
  406.                     }
  407.                 }
  408.  
  409.                 old_copy = prog->endp[0];    /* remember next character to be copied */
  410.                 /*
  411.                  * continue searching after the match
  412.                  * prevent endless loop with patterns that match empty strings,
  413.                  * e.g. :s/$/pat/g or :s/[a-z]* /(&)/g
  414.                  */
  415. skip:
  416.                 match = -1;
  417.                 lastone = (*old_match == NUL || got_int || got_quit || !do_all);
  418.                 if (lastone || do_ask ||
  419.                       (match = vim_regexec(prog, old_match, (int)FALSE)) == 0)
  420.                 {
  421.                     if (new_start)
  422.                     {
  423.                         /*
  424.                          * Copy the rest of the line, that didn't match.
  425.                          * Old_match has to be adjusted, we use the end of the
  426.                          * line as reference, because the substitute may have
  427.                          * changed the number of characters.
  428.                          */
  429.                         STRCAT(new_start, old_copy);
  430.                         i = old_line + STRLEN(old_line) - old_match;
  431.                         if (u_savesub(lnum) == OK)
  432.                             ml_replace(lnum, new_start, TRUE);
  433.  
  434.                         vim_free(old_line);            /* free the temp buffer */
  435.                         old_line = new_start;
  436.                         new_start = NULL;
  437.                         old_match = old_line + STRLEN(old_line) - i;
  438.                         if (old_match < old_line)        /* safety check */
  439.                         {
  440.                             EMSG("do_sub internal error: old_match < old_line");
  441.                             old_match = old_line;
  442.                         }
  443.                         old_copy = old_line;
  444.                     }
  445.                     if (match == -1 && !lastone)
  446.                         match = vim_regexec(prog, old_match, (int)FALSE);
  447.                     if (match <= 0)      /* quit loop if there is no more match */
  448.                         break;
  449.                 }
  450.                 line_breakcheck();
  451.  
  452.             }
  453.             if (did_sub)
  454.                 ++sub_nlines;
  455.             vim_free(old_line);        /* free the copy of the original line */
  456.             old_line = NULL;
  457.         }
  458.         line_breakcheck();
  459.     }
  460.  
  461. outofmem:
  462.     vim_free(old_line);        /* may have to free an allocated copy of the line */
  463.     if (sub_nsubs)
  464.     {
  465.         CHANGED;
  466.         if (!global_busy)
  467.         {
  468.             updateScreen(CURSUPD); /* need this to update LineSizes */
  469.             beginline(TRUE);
  470.             if (!do_sub_msg() && do_ask)
  471.                 MSG("");
  472.         }
  473.         if (do_print)
  474.             print_line(curwin->w_cursor.lnum, FALSE);
  475.     }
  476.     else if (!global_busy)
  477.     {
  478.         if (got_int)            /* interrupted */
  479.             emsg(e_interr);
  480.         else if (got_match)        /* did find something but nothing substituted */
  481.             MSG("");
  482.         else                    /* nothing found */
  483.             emsg(e_nomatch);
  484.     }
  485.  
  486.     vim_free(prog);
  487. }
  488.  
  489. /*
  490.  * Give message for number of substitutions.
  491.  * Can also be used after a ":global" command.
  492.  * Return TRUE if a message was given.
  493.  */
  494.     static int
  495. do_sub_msg()
  496. {
  497.     if (sub_nsubs > p_report)
  498.     {
  499.         sprintf((char *)msg_buf, "%s%ld substitution%s on %ld line%s",
  500.                 got_int ? "(Interrupted) " : "",
  501.                 sub_nsubs, plural(sub_nsubs),
  502.                 (long)sub_nlines, plural((long)sub_nlines));
  503.         if (msg(msg_buf))
  504.             keep_msg = msg_buf;
  505.         return TRUE;
  506.     }
  507.     if (got_int)
  508.     {
  509.         emsg(e_interr);
  510.         return TRUE;
  511.     }
  512.     return FALSE;
  513. }
  514.  
  515. /*
  516.  * do_glob(cmd)
  517.  *
  518.  * Execute a global command of the form:
  519.  *
  520.  * g/pattern/X : execute X on all lines where pattern matches
  521.  * v/pattern/X : execute X on all lines where pattern does not match
  522.  *
  523.  * where 'X' is an EX command
  524.  *
  525.  * The command character (as well as the trailing slash) is optional, and
  526.  * is assumed to be 'p' if missing.
  527.  *
  528.  * This is implemented in two passes: first we scan the file for the pattern and
  529.  * set a mark for each line that (not) matches. secondly we execute the command
  530.  * for each line that has a mark. This is required because after deleting
  531.  * lines we do not know where to search for the next match.
  532.  */
  533.  
  534.     void
  535. do_glob(type, lp, up, cmd)
  536.     int         type;
  537.     linenr_t    lp, up;
  538.     char_u        *cmd;
  539. {
  540.     linenr_t        lnum;        /* line number according to old situation */
  541.     linenr_t        old_lcount; /* curbuf->b_ml.ml_line_count before the command */
  542.     int             ndone;
  543.  
  544.     char_u            delim;        /* delimiter, normally '/' */
  545.     char_u           *pat;
  546.     regexp           *prog;
  547.     int                match;
  548.     int                which_pat;
  549.  
  550.     if (global_busy)
  551.     {
  552.         EMSG("Cannot do :global recursive");    /* will increment global_busy */
  553.         return;
  554.     }
  555.  
  556.     which_pat = RE_LAST;            /* default: use last used regexp */
  557.     sub_nsubs = 0;
  558.     sub_nlines = 0;
  559.  
  560.     /*
  561.      * undocumented vi feature:
  562.      *    "\/" and "\?": use previous search pattern.
  563.      *           "\&": use previous substitute pattern.
  564.      */
  565.     if (*cmd == '\\')
  566.     {
  567.         ++cmd;
  568.         if (vim_strchr((char_u *)"/?&", *cmd) == NULL)
  569.         {
  570.             emsg(e_backslash);
  571.             return;
  572.         }
  573.         if (*cmd == '&')
  574.             which_pat = RE_SUBST;        /* use previous substitute pattern */
  575.         else
  576.             which_pat = RE_SEARCH;        /* use previous search pattern */
  577.         ++cmd;
  578.         pat = (char_u *)"";
  579.     }
  580.     else if (*cmd == NUL)
  581.     {
  582.         EMSG("Regular expression missing from global");
  583.         return;
  584.     }
  585.     else
  586.     {
  587.         delim = *cmd;             /* get the delimiter */
  588.         if (delim)
  589.             ++cmd;                /* skip delimiter if there is one */
  590.         pat = cmd;                /* remember start of pattern */
  591.         cmd = skip_regexp(cmd, delim);
  592.         if (cmd[0] == delim)                /* end delimiter found */
  593.             *cmd++ = NUL;                    /* replace it with a NUL */
  594.     }
  595.  
  596.     if ((prog = myregcomp(pat, RE_BOTH, which_pat, SEARCH_HIS)) == NULL)
  597.     {
  598.         emsg(e_invcmd);
  599.         return;
  600.     }
  601.  
  602. /*
  603.  * pass 1: set marks for each (not) matching line
  604.  */
  605.     ndone = 0;
  606.     for (lnum = lp; lnum <= up && !got_int; ++lnum)
  607.     {
  608.         /* a match on this line? */
  609.         match = vim_regexec(prog, ml_get(lnum), (int)TRUE);
  610.         if ((type == 'g' && match) || (type == 'v' && !match))
  611.         {
  612.             ml_setmarked(lnum);
  613.             ndone++;
  614.         }
  615.         line_breakcheck();
  616.     }
  617.  
  618. /*
  619.  * pass 2: execute the command for each line that has been marked
  620.  */
  621.     if (got_int)
  622.         MSG("Interrupted");
  623.     else if (ndone == 0)
  624.         msg(e_nomatch);
  625.     else
  626.     {
  627.         /*
  628.          * Set current position only once for a global command.
  629.          * If global_busy is set, setpcmark() will not do anything.
  630.          * If there is an error, global_busy will be incremented.
  631.          */
  632.         setpcmark();
  633.  
  634.         global_busy = 1;
  635.         old_lcount = curbuf->b_ml.ml_line_count;
  636.         while (!got_int && (lnum = ml_firstmarked()) != 0 && global_busy == 1)
  637.         {
  638.             curwin->w_cursor.lnum = lnum;
  639.             curwin->w_cursor.col = 0;
  640.             if (*cmd == NUL || *cmd == '\n')
  641.                 do_cmdline((char_u *)"p", FALSE, TRUE);
  642.             else
  643.                 do_cmdline(cmd, FALSE, TRUE);
  644.             mch_breakcheck();
  645.         }
  646.  
  647.         global_busy = 0;
  648.  
  649.         must_redraw = CLEAR;
  650.         cursupdate();
  651.  
  652.         /* If subsitutes done, report number of substitues, otherwise report
  653.          * number of extra or deleted lines. */
  654.         if (!do_sub_msg())
  655.             msgmore(curbuf->b_ml.ml_line_count - old_lcount);
  656.     }
  657.  
  658.     ml_clearmarked();      /* clear rest of the marks */
  659.     vim_free(prog);
  660. }
  661.  
  662. #ifdef VIMINFO
  663.     int
  664. read_viminfo_sub_string(line, fp, force)
  665.     char_u    *line;
  666.     FILE    *fp;
  667.     int        force;
  668. {
  669.     if (old_sub != NULL && force)
  670.         vim_free(old_sub);
  671.     if (force || old_sub == NULL)
  672.     {
  673.         viminfo_readstring(line);
  674.         old_sub = strsave(line + 1);
  675.     }
  676.     return vim_fgets(line, LSIZE, fp);
  677. }
  678.  
  679.     void
  680. write_viminfo_sub_string(fp)
  681.     FILE    *fp;
  682. {
  683.     if (get_viminfo_parameter('/') != 0 && old_sub != NULL)
  684.     {
  685.         fprintf(fp, "\n# Last Substitute String:\n$");
  686.         viminfo_writestring(fp, old_sub);
  687.     }
  688. }
  689. #endif /* VIMINFO */
  690.